<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Spark • Procedural Splats</title>
  <style>
    body {
      margin: 0;
    }
    canvas {
      touch-action: none;
    }
  </style>
</head>

<body>
  <script type="importmap">
    {
      "imports": {
        "three": "/examples/js/vendor/three/build/three.module.js",
        "@sparkjsdev/spark": "/dist/spark.module.js"
      }
    }
  </script>
  <script type="module">
    import * as THREE from "three";
    import { SparkControls, SplatMesh, constructGrid, constructAxes, textSplats, imageSplats } from "@sparkjsdev/spark";
    import { getAssetFileURL } from "/examples/js/get-asset-url.js";

    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    window.addEventListener('resize', onWindowResize, false);
    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    // Generate a Sierpinski pyramid one splat at a time via iteration
    const pyramid = new SplatMesh({
      constructSplats: (splats) => {
        const NUM_SPLATS = 500000;
        const ITERATIONS = 15;
        const CORNERS = [
          new THREE.Vector3(0, 1, -0.7),
          new THREE.Vector3(0, 0, 0),
          new THREE.Vector3(0.6, 1, 0.5),
          new THREE.Vector3(-0.6, 1, 0.5),
        ];

        const center = new THREE.Vector3();
        const scales = new THREE.Vector3().setScalar(0.0005);
        const quaternion = new THREE.Quaternion();
        const opacity = 1;
        const color = new THREE.Color(1, 1, 1);

        for (let i = 0; i < NUM_SPLATS; i++) {
          center.set(0, 0, 0);
          for (let iter = 0; iter < ITERATIONS; iter++) {
            const corner = CORNERS[Math.floor(Math.random() * 4)];
            center.add(corner).multiplyScalar(0.5);
          }
          color.set(0.3 + center.x - center.z * 0.5, center.z * 1.5, 0.5 + center.z * 2);
          splats.pushSplat(center, scales, quaternion, opacity, color);
        }
      },
    });
    pyramid.position.set(0, -0.5, -2);
    scene.add(pyramid);

    const stars = new SplatMesh({
      constructSplats: (splats) => {
        const NUM_SPLATS = 100000;

        const center = new THREE.Vector3();
        const scales = new THREE.Vector3().setScalar(0.008);
        const quaternion = new THREE.Quaternion();
        const opacity = 1;
        const color = new THREE.Color(1, 1, 1);

        for (let i = 0; i < NUM_SPLATS; i++) {
          // Distribute stars on a 50x50x50 cube
          center.set(Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5).multiplyScalar(50);
          // Random purple-ish color
          color.set(0.4 + Math.random() * 0.3, 0.5, 0.7 + Math.random() * 0.3);
          splats.pushSplat(center, scales, quaternion, opacity, color);
        }
      },
    });
    stars.position.set(0, -0.5, -2);
    scene.add(stars);

    // Generate splats from rendered text
    const text = textSplats({
      text: "I        GSplats!",
      font: "Arial",
      fontSize: 60,
      color: new THREE.Color(0xE420B7),
    });
    text.scale.setScalar(0.5 / 80);
    text.position.set(0.15, -1, -2);
    scene.add(text);

    const text2 = textSplats({
      text: "WASD + mouse to move",
      font: "Verdana",
      fontSize: 30,
      color: new THREE.Color(0x49EAFF)
    });
    text2.scale.setScalar(0.2 / 40);
    text2.position.set(0, 1.0, -2);
    scene.add(text2);

    // Generate splats from image pixels with alpha channel
    const imageURL = await getAssetFileURL("butterfly.png");
    const image = imageSplats({ url: imageURL });
    image.scale.setScalar(0.7 / 400);
    image.position.set(-0.5, -1, -2);
    scene.add(image);

    const controls = new SparkControls({ canvas: renderer.domElement });

    renderer.setAnimationLoop(function animate(time) {
      controls.update(camera);
      renderer.render(scene, camera);

      pyramid.rotation.y = time / 5000;

      // Move stars back and forth
      stars.position.z = Math.sin(time / 10000) * 10;
      // Rotate butterfly at different rates on each axis
      image.rotation.set(
        -0.3 + 0.35 * Math.sin(time / 190),
        0.35 * Math.sin(time / 390),
        0.1 * Math.sin(time / 200)
      );
      // Oscilate butterfly y position
      image.position.y = -0.9 + 0.04 * Math.sin(time / 330);

      // Animate opacity of top text
      text2.opacity = Math.abs(Math.sin(time / 1000) * 0.3);
    });
  </script>
</body>

</html>
